# 为了简化函数的调用，Python中通过 @property 和 @xxx.setter 分别修改两个函数。
# 注意：
# 两个函数的函数名尽量和被私有化的属性名保持一致
# @xxx.setter 中的 xxx 需要和被 @property 修饰的函数名保持一致
# @property 修饰的函数用于获取值，@xxx.setter 修饰的函数用于修改值
# @property 和 @xxx.setter 并不是需要同时出现，根据自己的需求进行选择
# @property出现，@xxx.setter 可以不出现
# @xxx.setter出现，@property 必须出现


class Person(object):
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    # 获取值：将被私有化属性的值返回
    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = name

    @property
    def age(self):
        return self.__age

    # 修改值：设置参数，将参数赋值给私有化属性
    @age.setter
    def age(self, age):
        self.__age = age


# 将函数当做属性访问
p = Person('小明', 18)
print(p.name)
p.name = '小红'
print(p.name)


class Book(object):
    def a(self):
        print('a')
        # 注意：在类中，对象函数之间相互调用，语法：self.函数
        # 在类中，私有函数之间相互调用，语法：self.__函数
        self.__b()

    def __b(self):
        print('b')


print("--------" * 10)
b = Book()
b.a()
print("--------" * 10)


class Car:
    def __init__(self, name, price):
        print('我是父类构造器')
        self.__name = name
        self.__price = price

    def run(self):
        print('%s在跑' % self.__name)

    def __show(self):
        print('%s的价格是%s' % (self.__name, self.__price))


# 在子类中未定义构造函数，则创建子类对象默认调用父类中的构造函数
class XiaomiCar(Car):
    # 方式一:
    #     super(当前类, self).__init__(参数列表)
    #
    # 方式二:
    #     super().__init__(参数列表)
    #
    # 方式三:
    #     父类名.__init__(self, 参数列表)
    def __init__(self, name, price):
        # 方式一：
        # super(XiaomiCar, self).__init__(name, price)
        # 方式二：
        # super().__init__(name, price)
        # 方式三：
        Car.__init__(self, name, price)

        print('我是子类构造器')
        # 调用父类构造函数
        pass


xiaomi = XiaomiCar('小米', 10000)
# 注意：子类对象调用父类中公有方法
xiaomi.run()
# xiaomi.show()

xiaomi._Car__show()  # 访问私有属性


# xiaomi._XiaomiCar__show()  # 此处无法访问，因为子类中未定义构造函数，
# 则创建子类对象默认调用父类中的构造函数，此时父类中私有属性被隐藏，无法访问
# 子类无法继承父类私有属性，也不能调用私有方法


class Book:
    def __init__(self):
        print('我是父类构造器')

    def show(self):
        print('我是父类方法')


class PythonBook(Book):
    def __init__(self):
        # 方式一:
        #     super(当前类, self).__init__(参数列表)
        # super(PythonBook, self).__init__()
        # 
        # 方式二:
        #     super().__init__(参数列表)
        # super().__init__()
        #
        # 方式三:
        #     父类名.__init__(self, 参数列表)
        Book.__init__(self)
        print('我是子类构造器')


# 在子类中定义构造函数，默认情况下并不会自动调用父类的构造器，
# 为了确保父类的初始化也能被执行，你需要显式地调用父类的构造函数，使用super()函数或者直接通过父类名加构造函数的方式来实现。
print('--------' * 10)
p = PythonBook()
p.show()


class Aniaml(object):
    __slots__ = ("name", "age")


class Cat(Aniaml):
    pass


# a = Aniaml()
# a.name = '小花'
# a.age = 18
# a.sex = '女'

c = Cat()
# __slots__ 定义的属性仅对当前类的实例起作用，对继承的子类实例是不起作用
c.name = '小花'
c.age = 18
# 以下这种方式是动态绑定属性
c.sex = '公猫'
print(c.sex)


class A(object):
    # 如果子类本身也有 __slots__ 属性，实例化时对象只能是子类自身的 __slots__属性 加上父类的 __slots__

    __slots__ = ("name", "age")


class B(A):
    __slots__ = ("sex",)


# 如果子类与父类都使用了__slots__限定绑定属性，那么实例化的对象，只能使用子父类的 __slots__ 允许绑定的属性
b = B()
b.name = '小花'
b.sex = "nan"
b.age = 10
# b.a = 1
